// src/bigNumber.ts
import Print from "./print";
import {
  resolveAdd,
  resolveDivision,
  resolveMultiply,
  resolveSubtract,
} from "./resolve";
import {
  compare,
  createZeroStr,
  exchangeScienceString,
  getDecimalLength,
  getMaxDecimal,
  getResult,
  numRegex,
  scienceNumRegex,
} from "./utils";

/**
 * @description 转换科学计数器字符
 * @param nums @
 * @returns
 */
export const formatArguement = (nums: string) => {
  const newNums = nums;
  let strNum = newNums;
  // 非数字字符串，非科学计数法字符串，直接return false
  if (!numRegex.test(strNum) && !scienceNumRegex.test(strNum)) {
    Print.error(`存在非数字字符串参数：${strNum}`);
    return 0;
  }
  // 将存在科学计数法的数字转换成普通数字字符串
  if (scienceNumRegex.test(strNum)) {
    strNum = exchangeScienceString(strNum);
  }
  return newNums;
};

// 四则运算
export const add = (a: string, b: string) => {
  //TODO 先计算最长小数位
  const { value, decimalPlaces } = getMaxDecimal([a, b]);
  let resultArr: string[] | null = null;
  let sign = 1;
  const nums = [a, b];
  for (let i = 0; i < nums.length; i++) {
    let item = nums[i];
    // 存在小数项，需要对小于最长的小数位每一项进行补零操作
    if (value) {
      // 计算每一项小数位数
      if (item.indexOf(".") > -1) {
        const decimalLength = getDecimalLength(item);
        if (decimalLength < decimalPlaces) {
          item += createZeroStr(decimalPlaces - decimalLength);
        }
      } else {
        item += createZeroStr(decimalPlaces);
      }
    }
    // 获得正负
    const [, prefix] = item.match(/^([-+])?(.*)/) || [];
    // 剔除特殊字符（[.-+]）
    item = item.replace(/[\.\-\+]/g, "");
    const itemArr = item.split("");
    itemArr.reverse();
    if (resultArr === null) {
      resultArr = itemArr;
      if (prefix === "-") {
        sign = -1;
      }
      continue;
    } else {
      if (sign === -1 || prefix === "-") {
        // 同为负数执行加法操作
        if (prefix === "-" && sign === -1) {
          resultArr = resolveAdd(resultArr, itemArr);
        } else {
          // 存在正负数相加需要先比较两数大小，
          const lt = compare(resultArr, itemArr);
          if (lt >= 0) {
            resultArr = resolveSubtract(resultArr, itemArr);
          } else {
            if (sign === -1) {
              sign = 1;
            }
            if (prefix === "-") {
              sign = -1;
            }
            resultArr = resolveSubtract(itemArr, resultArr);
          }
        }
      } else {
        resultArr = resolveAdd(resultArr, itemArr);
      }
    }
  }
  return getResult(resultArr as string[], decimalPlaces, sign);
};

export const subtract = (a: string, b: string): string => {
  //TODO 先计算最长小数位
  const { value, decimalPlaces } = getMaxDecimal([a, b]);
  const nums = [a, b];
  let resultArr: string[] | null = null;
  let sign = 1;
  for (let i = 0; i < nums.length; i++) {
    let item = nums[i];
    // 存在小数项，需要对小于最长的小数位每一项进行补零操作
    if (value) {
      // 计算每一项小数位数
      if (item.indexOf(".") > -1) {
        const decimalLength = getDecimalLength(item);
        if (decimalLength < decimalPlaces) {
          item += createZeroStr(decimalPlaces - decimalLength);
        }
      } else {
        item += createZeroStr(decimalPlaces);
      }
    }
    // 获得正负
    const [, prefix] = item.match(/^([-+])?(.*)/) || [];
    // 剔除特殊字符（[.-+]）
    item = item.replace(/[\.\-\+]/g, "");
    const itemArr = item.split("");
    itemArr.reverse();
    if (resultArr === null) {
      resultArr = itemArr;
      if (prefix === "-") {
        sign = -1;
      }
      continue;
    } else {
      if (sign === -1 && prefix !== "-") {
        resultArr = resolveAdd(itemArr, resultArr);
      } else {
        const lt = compare(resultArr, itemArr);
        if (lt >= 0) {
          if (prefix === "-") {
            if (sign === 1) {
              resultArr = resolveAdd(resultArr, itemArr);
            } else {
              resultArr = resolveSubtract(resultArr, itemArr);
            }
          } else {
            resultArr = resolveSubtract(resultArr, itemArr);
          }
        } else {
          if (prefix === "-") {
            if (sign === 1) {
              resultArr = resolveAdd(itemArr, resultArr);
            } else {
              sign = 1;
              resultArr = resolveSubtract(itemArr, resultArr);
            }
          } else {
            sign = -1;
            resultArr = resolveSubtract(itemArr, resultArr);
          }
        }
      }
    }
  }
  return getResult(resultArr as string[], decimalPlaces, sign);
};

export const multiply = (a: string, b: string): string => {
  // 乘法无需补零，只需要将所有小数的小数位相加
  let decimalLength = 0;
  let resultArr: string[] | null = null;
  let sign = 1;
  const nums = [a, b];
  for (let i = 0; i < nums.length; i++) {
    let item = nums[i];
    // TODO: 乘数为 ±1直接返回
    if (i > 0 && (String(item) === "-1" || String(item) === "1")) {
      if (String(item) === "-1") {
        sign = sign * -1;
      }
      continue;
    }
    // 乘法无需补零，只需要将所有小数的小数位相加
    if (item.indexOf(".") > -1) {
      decimalLength += getDecimalLength(item);
    }
    // 获得正负
    const [, prefix] = item.match(/^([-+])?(.*)/) || [];
    // 剔除特殊字符（[.-+]）
    item = item.replace(/[\.\-\+]/g, "");
    const itemArr = item.split("");
    itemArr.reverse();
    if (resultArr === null) {
      resultArr = itemArr;
      if (prefix === "-") {
        sign = -1;
      }
      continue;
    } else {
      if (prefix === "-") {
        sign = sign * -1;
      }
      resultArr = resolveMultiply(resultArr, itemArr);
    }
  }
  return getResult(resultArr as string[], decimalLength, sign);
};

export const divide = (a: string, b: string): string => {
  /**
   * 除法每次需要进行被除数和除数小数位数比较，
   * 1、除数小数位数多于被除数小数位数，直接去除小数点
   * 2、被除数小数位数多于除数小数位，记录多出的小数位数，再对被除数去除小数操作，同时进行除数补零
   */

  let decimalLength = 0;
  let resultArr: string[] | null = null;
  let sign = 1;
  const nums = [a, b];
  for (let i = 0; i < nums.length; i++) {
    let item = nums[i];
    // TODO: 除数为 ±1直接返回
    if (i > 0 && (String(item) === "-1" || String(item) === "1")) {
      if (String(item) === "-1") {
        sign = sign * -1;
      }
      continue;
    }
    if (item.indexOf(".") > -1) {
      // 除法每次需要进行被除数和除数小数位数比较
      const divisDecimalLength = getDecimalLength(item);
      if (i > 0) {
        // 除数小数位数多于被除数小数位数，直接去除小数点
        decimalLength = decimalLength - divisDecimalLength;
      }
      if (i === 0) {
        decimalLength = getDecimalLength(item);
      }
    }

    // 获得正负
    const [, prefix] = item.match(/^([-+])?(.*)/) || [];
    // 剔除特殊字符（[.-+]）
    item = item.replace(/[\.\-\+]/g, "");
    const itemArr = item.split("");
    itemArr.reverse();
    if (resultArr === null) {
      resultArr = itemArr;
      if (prefix === "-") {
        sign = -1;
      }
      continue;
    } else {
      /**
       * 先比较被除数和除数大小
       * 1、被除数比除数小需要进行补0操作，及小数位数需要增加
       */
      if (prefix === "-") {
        sign = sign * -1;
      }
      // 除数小数位数大于被除数，需要被除数补零，小数位数清空
      if (decimalLength < 0) {
        const zeroLen = new Array(Math.abs(decimalLength)).fill(0);
        resultArr = zeroLen.concat(resultArr);
        decimalLength = 0;
      }
      const { result, decimalLength: newDecimalLength } = resolveDivision(
        resultArr,
        itemArr,
        decimalLength
      );
      resultArr = result as string[];
      decimalLength = newDecimalLength;
    }
  }
  return getResult(resultArr as string[], decimalLength, sign);
};

/**
 * 精确小数(目标小数位数小于最大精确位数直接返回原值，否则在精确位数+1上四舍五入)
 * @param {Number} num 待精确小数
 * @param {Number} min 最小位数
 * @param {Number} max 最大位数
 */
export const toFixeds = (
  num: number | string,
  min?: number,
  max?: number
): string => {
  const minFix = min || 2;
  const maxFix = max;
  let str = String(num).replace(/^-/, "");
  if (maxFix && maxFix < minFix) {
    return `${num}`;
  }
  const prefixSymbol = String(num).startsWith("-") ? "-" : "";
  const dotIndex = str.indexOf(".");
  if (dotIndex === -1) {
    let dotArr = ".";
    for (let i = 0; i < minFix; i++) {
      dotArr += "0";
    }
    return str + dotArr;
  }
  const maxLen = maxFix || minFix;
  let i = dotIndex + 1;
  let n = "";
  while (n.length < maxLen) {
    n += (str[i++] || "0") + "";
    if (!str[i] && n.length >= minFix) {
      break;
    }
  }

  const newStr = str.substring(0, dotIndex) + "." + n;
  let step = 0; //四舍五入进位
  if (i < str.length) {
    step = Number(str[i]) >= 5 ? 1 : 0;
  }
  const nums = newStr.split("");
  const numberNums = nums.map((item) => (item === "." ? "." : Number(item)));
  if (step > 0) {
    while (i > 0) {
      i--;
      if (numberNums[i] === ".") {
        continue;
      }
      const sum = Number(numberNums[i]) + Number(step);
      numberNums[i] = sum % 10;
      step = sum >= 10 ? 1 : 0;
      if (i === 0 && Number(numberNums[i]) === 0) {
        numberNums.splice(0, 0, 1);
      }
      if (step === 0) break;
    }
  }
  return `${prefixSymbol}` + numberNums.join("");
};
